using System;
using System.Collections.Generic;
using System.Linq;
using SDF.Core.Caching;
using SDF.Core.Data;
using SDF.Core.Domain.Projects;
using SDF.Services.Events;

namespace SDF.Services.Projects
{
    /// <summary>
    /// Project service
    /// </summary>
    public partial class ProjectService : IProjectService
    {
        #region Constants

        /// <summary>
        /// Key for caching
        /// </summary>
        private const string PROJECTS_ALL_KEY = "SDF.projects.all";
        /// <summary>
        /// Key for caching
        /// </summary>
        /// <remarks>
        /// {0} : project ID
        /// </remarks>
        private const string PROJECTS_BY_ID_KEY = "SDF.projects.id-{0}";
        /// <summary>
        /// Key pattern to clear cache
        /// </summary>
        private const string PROJECTS_PATTERN_KEY = "SDF.projects.";

        #endregion
        
        #region Fields
        
        private readonly IRepository<Project> _projectRepository;
        private readonly IEventPublisher _eventPublisher;
        private readonly ICacheManager _cacheManager;

        #endregion

        #region Ctor

        /// <summary>
        /// Ctor
        /// </summary>
        /// <param name="cacheManager">Cache manager</param>
        /// <param name="projectRepository">Project repository</param>
        /// <param name="eventPublisher">Event published</param>
        public ProjectService(ICacheManager cacheManager,
            IRepository<Project> projectRepository,
            IEventPublisher eventPublisher)
        {
            this._cacheManager = cacheManager;
            this._projectRepository = projectRepository;
            this._eventPublisher = eventPublisher;
        }

        #endregion

        #region Methods

        /// <summary>
        /// Deletes a project
        /// </summary>
        /// <param name="project">Project</param>
        public virtual void DeleteProject(Project project)
        {
            if (project == null)
                throw new ArgumentNullException("project");

            var allProjects = GetAllProjects();
            if (allProjects.Count == 1)
                throw new Exception("You cannot delete the only configured project");

            _projectRepository.Delete(project);

            _cacheManager.RemoveByPattern(PROJECTS_PATTERN_KEY);

            //event notification
            _eventPublisher.EntityDeleted(project);
        }

        /// <summary>
        /// Gets all projects
        /// </summary>
        /// <returns>Projects</returns>
        public virtual IList<Project> GetAllProjects()
        {
            string key = PROJECTS_ALL_KEY;
            return _cacheManager.Get(key, () =>
            {
                var query = from s in _projectRepository.Table
                            orderby s.DisplayOrder, s.Id
                            select s;
                var projects = query.ToList();
                return projects;
            });
        }

        /// <summary>
        /// Gets a project 
        /// </summary>
        /// <param name="projectId">Project identifier</param>
        /// <returns>Project</returns>
        public virtual Project GetProjectById(int projectId)
        {
            if (projectId == 0)
                return null;
            
            string key = string.Format(PROJECTS_BY_ID_KEY, projectId);
            return _cacheManager.Get(key, () => _projectRepository.GetById(projectId));
        }

        /// <summary>
        /// Inserts a project
        /// </summary>
        /// <param name="project">Project</param>
        public virtual void InsertProject(Project project)
        {
            if (project == null)
                throw new ArgumentNullException("project");

            _projectRepository.Insert(project);

            _cacheManager.RemoveByPattern(PROJECTS_PATTERN_KEY);

            //event notification
            _eventPublisher.EntityInserted(project);
        }

        /// <summary>
        /// Updates the project
        /// </summary>
        /// <param name="project">Project</param>
        public virtual void UpdateProject(Project project)
        {
            if (project == null)
                throw new ArgumentNullException("project");

            _projectRepository.Update(project);

            _cacheManager.RemoveByPattern(PROJECTS_PATTERN_KEY);

            //event notification
            _eventPublisher.EntityUpdated(project);
        }

        #endregion
    }
}